#include <wx/wx.h>
#include <wx/sizer.h>
#include <wx/glcanvas.h>
#include <iostream>
using namespace std;
#include "main.h"
 
// include OpenGL
#ifdef __WXMAC__
#include "OpenGL/glu.h"
#include "OpenGL/gl.h"
#else
#include <GL/glu.h>
#include <GL/gl.h>
#endif
 
GLuint texture[2];
wxImage *image;
class MyApp: public wxApp
{
    virtual bool OnInit();
 
    wxFrame *frame;
    BasicGLPane * glPane;
public:
      
};
 
IMPLEMENT_APP(MyApp)
 
bool MyApp::OnInit()
{
    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
    frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Hello GL World"), wxPoint(50,50), wxSize(640,480));
	
    int args[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0};
    
    glPane = new BasicGLPane( (wxFrame*) frame, args);
    sizer->Add(glPane, 1, wxEXPAND);

    frame->SetSizer(sizer);
    frame->SetAutoLayout(true);
	
    frame->Show();
    return true;
} 
 
BEGIN_EVENT_TABLE(BasicGLPane, wxGLCanvas)
EVT_MOTION(BasicGLPane::mouseMoved)
EVT_LEFT_DOWN(BasicGLPane::mouseDown)
EVT_LEFT_UP(BasicGLPane::mouseReleased)
EVT_RIGHT_DOWN(BasicGLPane::rightClick)
EVT_LEAVE_WINDOW(BasicGLPane::mouseLeftWindow)
EVT_SIZE(BasicGLPane::resized)
EVT_KEY_DOWN(BasicGLPane::keyPressed)
EVT_KEY_UP(BasicGLPane::keyReleased)
EVT_MOUSEWHEEL(BasicGLPane::mouseWheelMoved)
EVT_PAINT(BasicGLPane::render)
END_EVENT_TABLE()
 
 
// some useful events to use
void BasicGLPane::mouseMoved(wxMouseEvent& event) {}
void BasicGLPane::mouseDown(wxMouseEvent& event) {}
void BasicGLPane::mouseWheelMoved(wxMouseEvent& event) {}
void BasicGLPane::mouseReleased(wxMouseEvent& event) {}
void BasicGLPane::rightClick(wxMouseEvent& event) {}
void BasicGLPane::mouseLeftWindow(wxMouseEvent& event) {}
void BasicGLPane::keyPressed(wxKeyEvent& event) {}
void BasicGLPane::keyReleased(wxKeyEvent& event) {}

BasicGLPane::~BasicGLPane()
{
	cout << "Quiting..." << endl;
	glDeleteTextures(2, texture);
	delete image;
}
BasicGLPane::BasicGLPane(wxFrame* parent, int* args) 
	: wxGLCanvas(parent, wxID_ANY,  wxDefaultPosition, wxDefaultSize, 0, wxT("GLCanvas"),  args)
{
	image = new wxImage(wxT("image.bmp"));
}
 
void BasicGLPane::resized(wxSizeEvent& evt)
{
	wxGLCanvas::OnSize(evt);
	Refresh();
}
void BasicGLPane::prepare3DViewport(int topleft_x, int topleft_y, int bottomright_x, int bottomright_y)
{
	//  Inits the OpenGL viewport for drawing in 3D.
	glClear(GL_COLOR_BUFFER_BIT);
    	glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	static bool init;
	if (init == false) {
		void *data = image->GetData();
		int w = image->GetWidth();
		int h = image->GetHeight();
		if ((w & (w - 1)) != 0) {
			cout << "warning: image.bmp's width is not a power of 2" << endl;
		}
		if ((h & (h - 1)) != 0) {
			cout << "warning: image.bmp's height is not a power of 2" << endl;
		}
		glGenTextures(2, texture);
		cout << "texture id: " << texture[0] << endl;
		cout << "texture id: " << texture[1] << endl;
		glBindTexture(GL_TEXTURE_2D, texture[0]);
		//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		/*
		bool wrap = false;
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap? GL_REPEAT: GL_CLAMP);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap? GL_REPEAT: GL_CLAMP);
		*/
		//gluBuild2DMipmaps(GL_TEXTURE_2D, 3, surface->w, surface->h, GL_BGR, GL_UNSIGNED_BYTE, surface->pixels);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		init = true;
	}
	/*
	glDisable(GL_CULL_FACE);
	glFrontFace(GL_CCW);
	
	int w = bottomright_x - topleft_x;
	int h = bottomright_y - topleft_y;
	glViewport(topleft_x, topleft_y, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, w, h, 0, -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	*/
}
 
void BasicGLPane::prepare2DViewport(int topleft_x, int topleft_y, int bottomright_x, int bottomright_y)
{
	//  Inits the OpenGL viewport for drawing in 2D
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black Background
	glEnable(GL_TEXTURE_2D);   // textures
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glViewport(topleft_x, topleft_y, bottomright_x-topleft_x, bottomright_y-topleft_y);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
    
	gluOrtho2D(topleft_x, bottomright_x, bottomright_y, topleft_y);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}
 
int BasicGLPane::getWidth()
{
    return GetSize().x;
}
 
int BasicGLPane::getHeight()
{
    return GetSize().y;
}
 
void BasicGLPane::render( wxPaintEvent& evt )
{
	SetCurrent();
	//Must always be here.
	wxPaintDC dc(this); // only to be used in paint events. use wxClientDC to paint outside the paint event
	//Do we need this every time...?
	if (!GetContext())
		return;
	if (!IsShown()) 
		return;
	prepare3DViewport(0, 0, getWidth(), getHeight());
	/*
	glRasterPos2i(0, 0);
	glDrawPixels(image->GetWidth(), image->GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, image->GetData());
	*/
	// render loaded image
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glPushMatrix();
	glBegin(GL_QUADS);
		glTexCoord2d(0.0, 0.0);
		glVertex2d(-1.0, -1.0);
		glTexCoord2d(1.0, 0.0);
		glVertex2d(1.0, -1.0);
		glTexCoord2d(1.0, 1.0);
		glVertex2d(1.0, 1.0);
		glTexCoord2d(0.0, 1.0);
		glVertex2d(-1.0, 1.0);
	glEnd();
	glPopMatrix();
	glFlush();
	SwapBuffers();
}

